None trial
In [1300]:
import os
import warnings
warnings.simplefilter(action='ignore', category=FutureWarning)
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from sklearn.linear_model import LassoCV, LinearRegression
from numpy.random import poisson

from dash import Dash, html, dcc, Input, Output, callback, dash_table
import plotly.express as px
import plotly.figure_factory as ff
import dash_mantine_components as dmc
import plotly.graph_objects as go
from plotly.subplots import make_subplots
In [1301]:
# if os.environ['CONDA_DEFAULT_ENV'] != 'cleague':
#     os.system("conda activate cleague")
#     if os.environ['CONDA_DEFAULT_ENV'] != 'cleague':
#         os.system("conda env create -f environment.yml")
#         os.system("conda activate cleague")
# print(os.environ['CONDA_DEFAULT_ENV'])
In [1302]:
RENDER_MODE = 'notebook'
In [1303]:
!jupyter nbconvert --to html --template full trial.ipynb
[NbConvertApp] Converting notebook trial.ipynb to html
[NbConvertApp] WARNING | Alternative text is missing on 2 image(s).
[NbConvertApp] Writing 1049465 bytes to trial.html
In [1304]:
# PATH = r"C:\Users\Tong Chen Rong\Desktop\cleague\data\Fifa 23 Players Data.csv"
PATH = os.getcwd()+'/data/Fifa 23 Players Data.csv'
In [1305]:
df = pd.read_csv(PATH)
In [1306]:
df.head(20)
Out[1306]:
Known As Full Name Overall Potential Value(in Euro) Positions Played Best Position Nationality Image Link Age ... LM Rating CM Rating RM Rating LWB Rating CDM Rating RWB Rating LB Rating CB Rating RB Rating GK Rating
0 L. Messi Lionel Messi 91 91 54000000 RW CAM Argentina https://cdn.sofifa.net/players/158/023/23_60.png 35 ... 91 88 91 67 66 67 62 53 62 22
1 K. Benzema Karim Benzema 91 91 64000000 CF,ST CF France https://cdn.sofifa.net/players/165/153/23_60.png 34 ... 89 84 89 67 67 67 63 58 63 21
2 R. Lewandowski Robert Lewandowski 91 91 84000000 ST ST Poland https://cdn.sofifa.net/players/188/545/23_60.png 33 ... 86 83 86 67 69 67 64 63 64 22
3 K. De Bruyne Kevin De Bruyne 91 91 107500000 CM,CAM CM Belgium https://cdn.sofifa.net/players/192/985/23_60.png 31 ... 91 91 91 82 82 82 78 72 78 24
4 K. Mbappé Kylian Mbappé 91 95 190500000 ST,LW ST France https://cdn.sofifa.net/players/231/747/23_60.png 23 ... 92 84 92 70 66 70 66 57 66 21
5 M. Salah Mohamed Salah 90 90 115500000 RW RW Egypt https://cdn.sofifa.net/players/209/331/23_60.png 30 ... 90 85 90 74 71 74 70 61 70 25
6 T. Courtois Thibaut Courtois 90 91 90000000 GK GK Belgium https://cdn.sofifa.net/players/192/119/23_60.png 30 ... 34 35 34 32 34 32 32 32 32 90
7 M. Neuer Manuel Neuer 90 90 13500000 GK GK Germany https://cdn.sofifa.net/players/167/495/23_60.png 36 ... 47 53 47 39 46 39 38 37 38 90
8 Cristiano Ronaldo C. Ronaldo dos Santos Aveiro 90 90 41000000 ST ST Portugal https://cdn.sofifa.net/players/020/801/23_60.png 37 ... 87 81 87 65 62 65 61 56 61 23
9 V. van Dijk Virgil van Dijk 90 90 98000000 CB CB Netherlands https://cdn.sofifa.net/players/203/376/23_60.png 30 ... 73 79 73 83 88 83 85 90 85 23
10 H. Kane Harry Kane 89 89 105500000 ST ST England https://cdn.sofifa.net/players/202/126/23_60.png 28 ... 86 84 86 69 71 69 66 64 66 23
11 Neymar Jr Neymar da Silva Santos Jr. 89 89 99500000 LW LW Brazil https://cdn.sofifa.net/players/190/871/23_60.png 30 ... 89 85 89 69 66 69 64 53 64 23
12 H. Son Heung Min Son 89 89 101000000 LW,LM LW Korea Republic https://cdn.sofifa.net/players/200/104/23_60.png 29 ... 89 83 89 71 67 71 67 57 67 22
13 Casemiro Carlos Henrique Venancio Casimiro 89 89 86000000 CDM CDM Brazil https://cdn.sofifa.net/players/200/145/23_60.png 30 ... 76 84 76 83 89 83 84 89 84 24
14 J. Oblak Jan Oblak 89 91 85500000 GK GK Slovenia https://cdn.sofifa.net/players/200/389/23_60.png 29 ... 37 41 37 35 40 35 35 36 35 90
15 S. Mané Sadio Mané 89 89 99500000 LM,CF LM Senegal https://cdn.sofifa.net/players/208/722/23_60.png 30 ... 89 82 89 71 68 71 68 62 68 22
16 Ederson Ederson Santana de Moraes 89 91 88000000 GK GK Brazil https://cdn.sofifa.net/players/210/257/23_60.png 28 ... 47 52 47 40 47 40 39 38 39 90
17 J. Kimmich Joshua Kimmich 89 90 105500000 CDM,RB CDM Germany https://cdn.sofifa.net/players/212/622/23_60.png 27 ... 86 89 86 87 89 87 86 84 86 24
18 Alisson Alisson Ramses Becker 89 90 79000000 GK GK Brazil https://cdn.sofifa.net/players/212/831/23_60.png 29 ... 45 49 45 36 42 36 35 35 35 90
19 N. Kanté N'Golo Kanté 89 89 72000000 CDM,CM CDM France https://cdn.sofifa.net/players/215/914/23_60.png 31 ... 81 84 81 87 89 87 87 87 87 23

20 rows × 89 columns

In [1307]:
POSITIONS = df["Best Position"].unique()
In [1308]:
CAM = df[df["Best Position"] == "CAM"]
In [1309]:
Y = CAM["CAM Rating"]
In [1310]:
FEATURE_NAMES = ['Crossing',
       'Finishing', 'Heading Accuracy', 'Short Passing', 'Volleys',
       'Dribbling', 'Curve', 'Freekick Accuracy', 'LongPassing', 'BallControl',
       'Acceleration', 'Sprint Speed', 'Agility', 'Reactions', 'Balance',
       'Shot Power', 'Jumping', 'Stamina', 'Strength', 'Long Shots',
       'Aggression', 'Interceptions', 'Positioning', 'Vision', 'Penalties',
       'Composure', 'Marking', 'Standing Tackle', 'Sliding Tackle',
       'Goalkeeper Diving', 'Goalkeeper Handling', ' GoalkeeperKicking',
       'Goalkeeper Positioning', 'Goalkeeper Reflexes']
In [1311]:
POISITION_CODE = {"CF":"Center Forward",
                     "ST":"Striker",
                     "RW":"Right Winger",
                     "LW":"Left Winger",
                     "GK":"Goalkeeper",
                     "CB":"Center Back",
                     "CDM":"Central Defensive Midfielder",
                     "LB":"Left Back",
                     "RB":"Right Back",
                     "LWB":"Left Wing Back",
                     "RWB":"Right Wing Back",
                     "CAM":"Central Attacking Midfielder",
                     "CM":"Central Midfielder",
                     "LM":"Left Midfielder",
                     "RM":"Right Midfielder"}
In [1312]:
X = CAM[FEATURE_NAMES]
In [1313]:
model = LassoCV(cv=5, max_iter=5000)
clf = model.fit(X, Y)
In [1314]:
importance = pd.Series(clf.coef_)
plt.figure(figsize=(20,10))
plt.bar(x = FEATURE_NAMES, height=np.abs(importance))
plt.xticks(rotation = 75)
plt.axhline(y=0.025, color='k', linestyle='--')
plt.title("Feature Importance of {}".format(POISITION_CODE["CAM"]), fontdict={'fontsize':15})
plt.show()
No description has been provided for this image
In [1315]:
# np.sum(importance)
In [1316]:
# model2 = LinearRegression()
# clf2 = model2.fit(X, Y)
In [1317]:
# clf2.coef_
In [1318]:
def calculateImportance(positionCode:str):
       players = df[df["Best Position"] == positionCode]
       y = players[positionCode + ' Rating']
       X = players[FEATURE_NAMES]
       model = LassoCV()
       clf = model.fit(X, y)
       importance = np.abs(clf.coef_).tolist()
       return importance
In [1319]:
# calculateImportance("LW")
In [1320]:
role_weights = pd.DataFrame(index=POSITIONS, columns=FEATURE_NAMES)
for role in POSITIONS:
       role_weights.loc[role] = calculateImportance(role)
In [1321]:
# role_weights.loc["CAM", :]
In [1322]:
role_weights
Out[1322]:
Crossing Finishing Heading Accuracy Short Passing Volleys Dribbling Curve Freekick Accuracy LongPassing BallControl ... Penalties Composure Marking Standing Tackle Sliding Tackle Goalkeeper Diving Goalkeeper Handling GoalkeeperKicking Goalkeeper Positioning Goalkeeper Reflexes
CAM 0.005458 0.078258 0.0 0.149039 0.007621 0.150559 0.0 0.014378 0.032409 0.170041 ... 0.0115 0.0 0.0 0.001553 0.013952 0.008606 0.003828 0.008545 0.0 0.00428
CF 0.036481 0.105508 0.006299 0.076423 0.002509 0.110475 0.0 0.0 0.011789 0.19305 ... 0.009819 0.0 0.0 0.005041 0.0 0.0 0.0 0.0 0.0 0.0
ST 0.008423 0.225021 0.09095 0.063236 0.000014 0.07971 0.0 0.010574 0.001914 0.121737 ... 0.008641 0.013144 0.003661 0.001177 0.001197 0.004967 0.005106 0.010331 0.007182 0.013566
CM 0.007827 0.019249 0.012676 0.172745 0.006806 0.073538 0.0 0.01327 0.155429 0.163861 ... 0.020229 0.000509 0.003619 0.063621 0.013635 0.012598 0.004624 0.00728 0.010053 0.022411
RW 0.097452 0.082667 0.006883 0.091147 0.002446 0.177249 0.007685 0.00265 0.000931 0.164213 ... 0.008306 0.000903 0.000791 0.00085 0.004477 0.0 0.014245 0.0 0.0 0.0
GK 0.0 0.008462 0.0 0.003796 0.002512 0.0 0.005046 0.0 0.0 0.0 ... 0.001624 0.006811 0.005693 0.0 0.0 0.213882 0.211593 0.050766 0.191833 0.225915
CB 0.00951 0.003719 0.083438 0.061257 0.0 0.008799 0.001572 0.001008 0.002055 0.048055 ... 0.0 0.015446 0.129078 0.214567 0.094047 0.014846 0.012726 0.005609 0.010795 0.004782
LW 0.100138 0.082873 0.006305 0.071299 0.007428 0.176652 0.0 0.003617 0.0 0.145505 ... 0.0 0.001439 0.000741 0.002667 0.0 0.01412 0.015292 0.0 0.002883 0.0
CDM 0.002961 0.000207 0.008536 0.157685 0.005903 0.014046 0.000235 0.007076 0.105972 0.099311 ... 0.007757 0.000338 0.096636 0.127284 0.057116 0.010548 0.011712 0.0 0.0 0.001594
LM 0.108998 0.060873 0.000753 0.116251 0.0 0.162695 0.003982 0.006229 0.034035 0.142298 ... 0.013641 0.0 0.00186 0.010495 0.001622 0.003656 0.00936 0.00961 0.008656 0.004243
LB 0.092197 0.000614 0.035556 0.071081 0.000379 0.006857 0.0 0.00016 0.00112 0.071744 ... 0.0 0.0 0.075702 0.115549 0.135979 0.0 0.0 0.0 0.0 0.0
RM 0.084145 0.061893 0.000422 0.117338 0.006571 0.178659 0.001957 0.012025 0.035384 0.142381 ... 0.009162 0.0 0.006838 0.0 0.008788 0.013187 0.011507 0.009565 0.0 0.015745
RB 0.090126 0.0 0.035205 0.072311 0.0 0.004911 0.000434 0.0 0.0 0.072799 ... 0.000554 0.0 0.069903 0.119344 0.133971 0.0 0.0 0.0 0.0 0.0
LWB 0.121573 0.003006 0.001176 0.10248 0.000752 0.061919 0.0 0.0 0.000955 0.073306 ... 0.000413 0.000145 0.055596 0.110105 0.089167 0.0 0.001007 0.0 0.0014 0.0
RWB 0.129774 0.000985 0.003047 0.093639 0.00689 0.049285 0.000907 0.001936 0.0 0.074073 ... 0.0 0.0 0.065767 0.109662 0.100222 0.004097 0.0 0.0 0.000191 0.0

15 rows × 34 columns

In [1323]:
role_weights_filter = role_weights.mask(role_weights<0.01, 0)
In [1324]:
role_sum = np.sum(role_weights_filter, axis=1)
In [1325]:
traits_sum = np.sum(role_weights_filter, axis=0)
In [1326]:
redundant_traits = traits_sum[traits_sum==0]
df = df.drop(redundant_traits.index.to_list(), axis=1)
role_weights_filter = role_weights_filter.drop(redundant_traits.index.to_list(), axis=1)
FEATURE_NAMES = [FEATURES for FEATURES in FEATURE_NAMES if FEATURES not in redundant_traits.index]
In [1327]:
# redundant_traits.index.any()
In [1328]:
# FEATURE_NAMES

Normalising Weights

In [1329]:
ROLE_WEIGHTS_NORM = role_weights_filter.divide(role_sum, axis='rows')
In [1330]:
importance = ROLE_WEIGHTS_NORM.loc["CAM"]
plt.figure(figsize=(20,10))
plt.bar(x = FEATURE_NAMES, height=np.abs(importance))
plt.xticks(rotation = 75)
plt.axhline(y=0.025, color='k', linestyle='--')
plt.title("Feature Importance of {}".format(POISITION_CODE["CAM"]), fontdict={'fontsize':15})
plt.show()
No description has been provided for this image
In [1331]:
# role_sum
In [1332]:
fig = px.bar(x=role_sum.index, y=role_sum, text_auto=True)
fig.update_layout(title='Bar Chart showing sum of Attribute Weights by Role',
                    xaxis_title='Roles',
                    yaxis_title='Total Weight',
                    width=1000,
                    height=500,
                    margin=dict(r=10, b=10, l=10, t=30), )
fig.show(renderer=RENDER_MODE)
In [1333]:
role_sum_norm = np.sum(ROLE_WEIGHTS_NORM, axis=1)
In [1334]:
# role_sum_norm
fig = px.bar(x=role_sum_norm.index, y=role_sum_norm, text_auto=True)
fig.update_layout(title='Bar Chart showing sum of Attribute Weights by Role after Normalisation',
                    xaxis_title='Role',
                    yaxis_title='Total Weight',
                    width=1000,
                    height=500,
                    margin=dict(r=10, b=10, l=10, t=30), )
fig.show(renderer=RENDER_MODE)
In [1335]:
def visualiseFeature(positionCode:str):
       weight = ROLE_WEIGHTS_NORM.loc[positionCode]
       plt.figure(figsize=(15,10))
       plt.bar(x = FEATURE_NAMES, height=weight)
       plt.xticks(rotation = 75)
       plt.axhline(y=0.01, color='k', linestyle='--')
       plt.title("Feature Importance of {}".format(POISITION_CODE[positionCode]), fontdict={'fontsize':15})
       plt.savefig(os.getcwd()+"/figures/{}_featureimportance.png".format(positionCode))
       # plt.show()
       plt.close()
In [1336]:
for position in POSITIONS:
    visualiseFeature(position)

Team Creator

In [1373]:
ALL_TEAMS = df["Club Name"].unique().tolist()
In [1374]:
def getPlayersFromTeam(teamName:str):
    players = df[df["Club Name"]==teamName]
    return players
In [1375]:
# getPlayersFromTeam("Chelsea")
In [1376]:
def getPlayersFromPosition(teamName:str, positionCode:str):
    players = getPlayersFromTeam(teamName)
    players_position = players[(players["Best Position"]==positionCode) | (players["Positions Played"].str.contains(positionCode))]
    return players_position
In [1377]:
# getPlayersFromPosition("Paris Saint-Germain", "CAM")
In [1378]:
# df["Positions Played"].str.contains("CAM")
In [1379]:
# df["Best Position"]=="CAM"

Goals Score Probability Calculator

Teams Creator

In [1337]:
POSITION_CONTRIBUTION = pd.read_csv(os.getcwd()+'/data/position_contribution.csv')
POSITION_CONTRIBUTION = POSITION_CONTRIBUTION.set_index('Unnamed: 0')
In [1338]:
# POSITION_CONTRIBUTION.loc["LW"]
In [1339]:
getPlayersFromTeam("Arsenal")
Out[1339]:
Known As Full Name Overall Potential Value(in Euro) Positions Played Best Position Nationality Image Link Age ... LM Rating CM Rating RM Rating LWB Rating CDM Rating RWB Rating LB Rating CB Rating RB Rating GK Rating
99 T. Partey Thomas Partey 84 84 36000000 CDM,CM CDM Ghana https://cdn.sofifa.net/players/209/989/23_60.png 29 ... 80 84 80 82 84 82 81 83 81 20
151 M. Ødegaard Martin Ødegaard 84 89 63500000 CAM,CM CAM Norway https://cdn.sofifa.net/players/222/665/23_60.png 23 ... 85 84 85 75 74 75 72 64 72 22
164 Gabriel Jesus Gabriel Fernando de Jesus 83 86 48500000 ST,RW ST Brazil https://cdn.sofifa.net/players/230/666/23_60.png 25 ... 84 77 84 65 62 65 61 56 61 20
229 B. Saka Bukayo Saka 82 89 60500000 RM,LM LM England https://cdn.sofifa.net/players/246/669/23_60.png 20 ... 83 79 83 78 74 78 76 68 76 19
235 A. Ramsdale Aaron Ramsdale 82 86 34500000 GK GK England https://cdn.sofifa.net/players/233/934/23_60.png 24 ... 40 45 40 35 41 35 34 33 34 83
322 K. Tierney Kieran Tierney 81 85 33000000 LB LWB Scotland https://cdn.sofifa.net/players/226/491/23_60.png 25 ... 79 77 79 82 79 82 81 78 81 22
325 Gabriel Gabriel dos S. Magalhães 81 87 37000000 CB CB Brazil https://cdn.sofifa.net/players/232/580/23_60.png 24 ... 61 68 61 72 78 72 75 83 75 19
412 W. Saliba William Saliba 80 87 40000000 CB CB France https://cdn.sofifa.net/players/243/715/23_60.png 21 ... 70 72 70 78 80 78 79 82 79 19
416 E. Smith Rowe Emile Smith Rowe 80 87 43000000 LM,CAM CAM England https://cdn.sofifa.net/players/240/273/23_60.png 21 ... 81 75 81 62 59 62 57 46 57 19
561 O. Zinchenko Oleksandr Zinchenko 79 82 21500000 LB CM Ukraine https://cdn.sofifa.net/players/227/813/23_60.png 25 ... 79 80 79 79 80 79 79 77 79 21
580 B. White Benjamin White 79 86 32500000 CB CDM England https://cdn.sofifa.net/players/231/936/23_60.png 24 ... 68 73 68 77 81 77 78 81 78 18
607 T. Tomiyasu Takehiro Tomiyasu 79 85 26000000 RB CB Japan https://cdn.sofifa.net/players/232/938/23_60.png 23 ... 70 72 70 78 79 78 79 80 79 19
624 G. Xhaka Granit Xhaka 79 79 15000000 CDM,CM CDM Switzerland https://cdn.sofifa.net/players/199/503/23_60.png 29 ... 73 79 73 74 79 74 73 74 73 19
686 Gabriel Martinelli Gabriel Teodoro Martinelli Silva 78 88 31500000 LM RM Brazil https://cdn.sofifa.net/players/251/566/23_60.png 21 ... 79 71 79 66 61 66 64 57 64 17
897 M. Turner Matt Turner 77 80 9500000 GK GK United States https://cdn.sofifa.net/players/233/267/23_60.png 28 ... 31 33 31 28 31 28 27 28 27 78
998 Fábio Vieira Fábio Daniel Ferreira Vieira 77 87 23500000 CAM,RM CAM Portugal https://cdn.sofifa.net/players/256/958/23_60.png 22 ... 77 74 77 62 60 62 58 49 58 17
1032 R. Holding Rob Holding 77 79 11500000 CB CB England https://cdn.sofifa.net/players/228/295/23_60.png 26 ... 62 67 62 71 76 71 72 79 72 22
1368 M. Elneny Mohamed Elneny 76 76 6500000 CDM,CM CDM Egypt https://cdn.sofifa.net/players/211/454/23_60.png 29 ... 71 75 71 74 76 74 73 74 73 19
1643 A. Sambi Lokonga Albert Sambi Lokonga 75 83 12500000 CM,CDM CM Belgium https://cdn.sofifa.net/players/241/928/23_60.png 22 ... 75 77 75 75 76 75 74 72 74 19
1734 Cédric Cédric Ricardo Alves Soares 75 75 4800000 RB RB Portugal https://cdn.sofifa.net/players/201/118/23_60.png 30 ... 73 73 73 75 74 75 75 73 75 21
2415 R. Nelson Reiss Nelson 73 80 6000000 RW,LW RM England https://cdn.sofifa.net/players/231/448/23_60.png 22 ... 75 68 75 62 56 62 58 47 58 20
2827 Marquinhos Marcus Vincius Oliveira Alencar 73 86 7000000 RM,LM LM Brazil https://cdn.sofifa.net/players/270/390/23_60.png 19 ... 74 66 74 58 53 58 55 45 55 16
2933 E. Nketiah Eddie Nketiah 73 80 6000000 ST ST England https://cdn.sofifa.net/players/236/988/23_60.png 23 ... 71 63 71 51 48 51 48 42 48 18
10855 M. Smith Matthew Smith 64 74 1300000 CM,CDM CM England https://cdn.sofifa.net/players/247/731/23_60.png 21 ... 64 66 64 64 65 64 63 61 63 20
10943 K. Hein Karl Hein 64 77 1200000 GK GK Estonia https://cdn.sofifa.net/players/248/732/23_60.png 20 ... 24 25 24 25 25 25 25 24 25 65
13288 C. Cîrjan Cătălin Cîrjan 62 78 1000000 CM,RM CAM Romania https://cdn.sofifa.net/players/260/141/23_60.png 19 ... 64 64 64 55 56 55 54 49 54 19
14095 J. Ideho Joel Ideho 61 78 825000 LM,RM,CF RM Netherlands https://cdn.sofifa.net/players/259/403/23_60.png 18 ... 62 55 62 53 46 53 51 43 51 14
15912 A. Cozier-Duberry Amario Cozier-Duberry 59 78 550000 LM,CAM LM England https://cdn.sofifa.net/players/271/743/23_60.png 17 ... 60 53 60 48 44 48 45 39 45 17
16986 L. Sousa Lino Sousa 56 80 425000 LB LB England https://cdn.sofifa.net/players/271/808/23_60.png 17 ... 52 48 52 56 52 56 56 56 56 17

29 rows × 86 columns

In [1340]:
# SAMPLE_TEAM = {"NAME":"POSITIONCODE"}

SAMPLE_TEAM_CHELSEA = {"Édouard Mendy":"GK",
                       "Kalidou Koulibaly":"CB",
                       "Thiago Emiliano da Silva":"CB",
                       "Reece James":"RWB",
                       "Ben Chilwell":"LWB",
                       "N'Golo Kanté":"CDM",
                       "Luiz Frello Filho Jorge":"CM",
                       "Mateo Kovačić":"CM",
                       "Raheem Sterling":"RW",
                       "Christian Pulisic":"LW",
                       "Pierre-Emerick Aubameyang":"ST"}

SAMPLE_TEAM_ARSENAL = {"Aaron Ramsdale":"GK",
                       "Gabriel dos S. Magalhães":"CB",
                       "William Saliba":"CB",
                       "Takehiro Tomiyasu":"CB",
                       "Kieran Tierney":"LWB",
                       "Thomas Partey":"CDM",
                       "Martin Ødegaard":"CAM",
                       "Bukayo Saka":"LM",
                       "Emile Smith Rowe":"CAM",
                       "Oleksandr Zinchenko":"CM",
                       "Gabriel Fernando de Jesus":"ST"}
In [1341]:
# calculatePlayerScore(player:str, role:str)
#     Calculates Player's attack and defense scores. Player's attributes are weighted by role he is playing in. Scores are weighted by role's contribution to teams attack and defense
#     Attributes:
#         player:str
#             Player's fullname
#         role:str
#             Position code of role
#     Return:
#         attack_score: total attack score of player
#         defense_score: total defense score of player
#         player_attributes_weighted: List of weighted attributes of player

def calculatePlayerScore(player:str, role:str):
        player_attributes = df.loc[df["Full Name"]==player][FEATURE_NAMES]
        weight = ROLE_WEIGHTS_NORM.loc[role]
        player_attributes_weighted = player_attributes * weight
        player_attributes_weighted = player_attributes_weighted.iloc[0].to_list()
        player_score = np.dot(player_attributes, weight)[0]
        role_contribution = POSITION_CONTRIBUTION.loc[role]
        attack_score = player_score * role_contribution.loc["Attack"]
        defense_score = player_score * role_contribution.loc["Defense"]
        return attack_score, defense_score, player_attributes_weighted
        # return player_attributes_weighted
        # return player_attributes
In [1342]:
# type(calculatePlayerScore( "Gabriel dos S. Magalhães","CB")[2])
In [1343]:
def calculateTeamScore(team:dict):
    team_performance = pd.DataFrame(columns=["Position","Full Name", "Attack Score", "Defense Score"]+FEATURE_NAMES)
    players = team.items()
    for player, role in players:
        attack_indiv, defense_indiv, attributes_indiv = calculatePlayerScore(player, role)
        team_performance.loc[len(team_performance)] = [role, player, attack_indiv, defense_indiv]+attributes_indiv
    team_performance.insert(2, "Total Score", team_performance["Attack Score"]+team_performance["Defense Score"])
    return team_performance
In [1344]:
CHELSEA = calculateTeamScore(SAMPLE_TEAM_CHELSEA)
ARSENAL = calculateTeamScore(SAMPLE_TEAM_ARSENAL)
In [1345]:
ARSENAL
Out[1345]:
Position Full Name Total Score Attack Score Defense Score Crossing Finishing Heading Accuracy Short Passing Dribbling ... Penalties Composure Marking Standing Tackle Sliding Tackle Goalkeeper Diving Goalkeeper Handling GoalkeeperKicking Goalkeeper Positioning Goalkeeper Reflexes
0 GK Aaron Ramsdale 80.783991 0.000000 80.783991 0.000000 0.000000 0.000000 0.000000 0.000000 ... 0.000000 0.000000 0.000000 0.000000 0.000000 17.827152 16.361435 4.333357 15.026107 19.056990
1 CB Gabriel dos S. Magalhães 77.470890 0.000000 77.470890 0.000000 0.000000 6.588977 4.268285 0.000000 ... 0.000000 1.061903 9.953318 16.744781 6.989880 0.165513 0.094585 0.000000 0.100286 0.000000
2 CB William Saliba 76.622905 0.000000 76.622905 0.000000 0.000000 5.193665 4.439016 0.000000 ... 0.000000 1.090603 9.593560 16.744781 6.902506 0.096549 0.118231 0.000000 0.080229 0.000000
3 CB Takehiro Tomiyasu 75.319387 0.000000 75.319387 0.000000 0.000000 5.736286 4.268285 0.000000 ... 0.000000 1.033203 9.713479 16.346095 6.902506 0.124135 0.165523 0.000000 0.050143 0.000000
4 LWB Kieran Tierney 79.788460 7.978846 71.809614 10.076473 0.000000 0.000000 7.982337 4.884779 ... 0.000000 0.000000 4.330442 8.576229 6.856266 0.000000 0.000000 0.000000 0.000000 0.000000
5 CDM Thomas Partey 81.048204 8.104820 72.943383 0.000000 0.000000 0.000000 12.781520 1.125165 ... 0.000000 0.000000 7.372310 10.195896 4.357301 0.110648 0.145198 0.000000 0.000000 0.000000
6 CAM Martin Ødegaard 81.761810 73.585629 8.176181 0.000000 5.640869 0.000000 12.604774 12.299278 ... 0.696314 0.000000 0.000000 0.000000 0.683839 0.000000 0.000000 0.000000 0.000000 0.000000
7 LM Bukayo Saka 81.095212 48.657127 32.438085 8.486939 4.505671 0.000000 8.939907 13.137096 ... 0.917868 0.000000 0.000000 0.696127 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000
8 CAM Emile Smith Rowe 77.922152 70.129937 7.792215 0.000000 5.565657 0.000000 11.602122 12.154581 ... 0.707366 0.000000 0.000000 0.000000 0.415667 0.000000 0.000000 0.000000 0.000000 0.000000
9 CM Oleksandr Zinchenko 75.133981 45.080389 30.053592 0.000000 0.967742 0.769152 12.128980 5.099577 ... 1.034574 0.000000 0.000000 4.301623 0.898250 0.163800 0.000000 0.000000 0.095857 0.213695
10 ST Gabriel Fernando de Jesus 80.887880 80.887880 0.000000 0.000000 17.414623 6.614739 4.599071 6.466169 ... 0.000000 0.955916 0.000000 0.000000 0.000000 0.000000 0.000000 0.057797 0.000000 0.189740

11 rows × 36 columns

In [1346]:
fig = px.icicle(ARSENAL.melt(id_vars=['Full Name'], value_vars=['Attack Score', 'Defense Score'])
, path=[px.Constant("Total Score"), 'variable', 'Full Name'], values='value', width=800, height=800, )
fig.update_traces(root_color="lightgrey")
fig.update_layout(margin = dict(t=50, l=25, r=25, b=25), title="Chart showing Arsenal Players' Contribution to Attack and Defense")
fig.show(renderer=RENDER_MODE)
In [1347]:
fig = px.sunburst(ARSENAL.melt(id_vars=['Position','Full Name'], value_vars=FEATURE_NAMES), path=[px.Constant("Total Score"),'Position','Full Name', 'variable'], values='value', width=800, height=800)
fig.update_traces(root_color="lightgrey")
fig.update_layout(margin = dict(t=50, l=25, r=25, b=25), title="Sunburst Chart showing Arsenal Players' Attributes")
fig.show(renderer=RENDER_MODE)
In [1348]:
chelsea_attack = np.sum(CHELSEA["Attack Score"])
chelsea_defense = np.sum(CHELSEA["Defense Score"])
arsenal_attack = np.sum(ARSENAL["Attack Score"])
arsenal_defense = np.sum(ARSENAL["Defense Score"])
In [1349]:
arsenal_lambda = arsenal_attack - chelsea_defense
chelsea_lambda = chelsea_attack - arsenal_attack
In [1350]:
arsenal_lambda
Out[1350]:
-198.03788386529294
In [1351]:
chelsea_lambda
Out[1351]:
30.7231588776001
In [1352]:
MAX = 400
MIN = -400
SCALE_FACTOR = 5
AWAY_FACTOR = 0.95
HOME_FACTOR = 1.05

def calculateLambda(hometeam:pd.DataFrame, awayteam:pd.DataFrame):
    home_attack = np.sum(hometeam["Attack Score"]) * HOME_FACTOR
    home_defense = np.sum(hometeam["Defense Score"]) * HOME_FACTOR
    away_attack = np.sum(awayteam["Attack Score"]) * AWAY_FACTOR
    away_defense = np.sum(awayteam["Defense Score"]) * AWAY_FACTOR

    home_lambda_raw = home_attack - away_defense
    away_lambda_raw = away_attack - home_defense
    home_lambda = (home_lambda_raw-MIN) / (MAX-MIN) * SCALE_FACTOR
    away_lambda = (away_lambda_raw-MIN) / (MAX-MIN) * SCALE_FACTOR
    return home_lambda, away_lambda
In [1353]:
CHELSEA_LAMBDA, ARSENAL_LAMBDA = calculateLambda(CHELSEA, ARSENAL)
In [1354]:
# x = np.random.poisson(1.1777696411145124, 10000)
# y = np.random.poisson(0.39078506070956487, 10000)
# 
# np.sum(x > y)
In [1355]:
def calculateMatchOutcome(homelambda, awaylambda, iterations):
    home_goal = poisson(homelambda, iterations)
    away_goal = poisson(awaylambda, iterations)
    homeWin = np.sum(home_goal > away_goal)
    draw = np.sum(home_goal == away_goal)
    awayWin = np.sum(away_goal > home_goal)
    return np.array((home_goal, away_goal)).T, (homeWin, draw, awayWin)
In [1356]:
CHELSEA_ARSENAL = calculateMatchOutcome(CHELSEA_LAMBDA, ARSENAL_LAMBDA, 20000)
In [1357]:
# CHELSEA_SCORECOUNT = pd.Series(CHELSEA_ARSENAL[0][:,0]).value_counts()
# ARSENAL_SCORECOUNT = pd.Series(CHELSEA_ARSENAL[0][:,1]).value_counts()
# SCORE_COUNT = pd.DataFrame(columns=['Chelsea', 'Arsenal'])
# SCORE_COUNT['Chelsea'] = CHELSEA_SCORECOUNT
# SCORE_COUNT['Arsenal'] = ARSENAL_SCORECOUNT
# SCORE_COUNT = SCORE_COUNT.fillna(0.0).reset_index()

SCORE_COUNT = pd.DataFrame(CHELSEA_ARSENAL[0], columns=['Chelsea', 'Arsenal'])

fig = px.histogram(SCORE_COUNT, marginal="box", width=800, height=800, text_auto=True)
fig.update_layout(margin = dict(t=50, l=25, r=25, b=25), title="Histogram showing counts of Number of Goals Scored")
fig.show(renderer=RENDER_MODE)

Odds Calculation

In [1358]:
def winProbability(countOutcome:tuple):
    total = sum(countOutcome)
    probability = [outcome/total for outcome in countOutcome ]
    return tuple(probability)
In [1359]:
MATCH_PROBABILITIES = winProbability(CHELSEA_ARSENAL[1])
In [1360]:
fig = px.bar(x=['Chelsea Win', 'Draw', 'Arsenal Win'], y=MATCH_PROBABILITIES, color=['Chelsea Win', 'Draw', 'Arsenal Win'], color_discrete_sequence=['blue', 'gray', 'red'], text_auto=True)
fig.update_layout(title='Bar Chart showing Probability Distribution of Outcomes',
                    xaxis_title='Outcomes',
                    yaxis_title='Probability',
                    width=1000,
                    height=700,
                    margin=dict(r=10, b=10, l=10, t=30), )
fig.show(renderer=RENDER_MODE)
In [1361]:
def scoreProbability(scoretable:np.ndarray):
    scoretable_df = pd.DataFrame(scoretable, columns=['Home', 'Away']).groupby(['Home','Away']).size().reset_index(name='Count')
In [1362]:
x = pd.DataFrame(CHELSEA_ARSENAL[0], columns=['Home', 'Away'], )
x = x.groupby(['Home','Away']).size().reset_index(name='Count')
In [1363]:
def calculateRawOdds(probability:tuple):
    return tuple([1/prob for prob in probability])
In [1364]:
RAW_PROBABILITY = winProbability(calculateMatchOutcome(1.7291590385448554, 0.9913609944152171, 20000)[1])
RAW_ODDS = calculateRawOdds(RAW_PROBABILITY)
In [1365]:
RAW_ODDS
Out[1365]:
(1.831334126911455, 4.253509145044662, 4.569339730408956)
In [1366]:
RAW_PROBABILITY
Out[1366]:
(0.54605, 0.2351, 0.21885)
In [1367]:
MARGIN_ODDS = (1.82131232132, 4, 4.5)
In [1368]:
def expectedProfit(odds, betpercentage, probability):
    profit = sum([(1-odds[i]*betpercentage[i])*probability[i] for i in range(len(odds))])
        # sum([((betpercentage[(i+1)%3])+(betpercentage[(i+2)%3]))*(1-1/odds[i]) - (odds[i]*betpercentage[i])*(1/odds[i]) for i in range(len(odds))]))
    return profit
In [1369]:
def setMargin(originial_odds, margin, betpercentage, probability):
    individual_contribution = margin/len(originial_odds)
    margin_odds = []
    for i in range(len(originial_odds)):
        originial_PL = (1-originial_odds[i]*betpercentage[i])*probability[i]
        new_odds = (1-(originial_PL+individual_contribution)/probability[i])/betpercentage[i]
        margin_odds.append(new_odds)
    return tuple(margin_odds)
In [1370]:
home = np.linspace(0, 1, 100)
rest = np.ones(100) - home
DRAW_DIST = np.linspace(0,1,11).round(1)
AWAY_DIST = 1 - DRAW_DIST
profitTable = pd.DataFrame(columns=['Proportion Draw', 'Home', 'Away+Draw', 'Profit'])

for dist in range(len(DRAW_DIST)):
    draw = rest * DRAW_DIST[dist]
    away = rest * AWAY_DIST[dist]
    Grids = np.array((home, draw, away))
    profit = []
    for i in range(100):
        profit.append(expectedProfit(MARGIN_ODDS, Grids[:,i].tolist(), RAW_PROBABILITY))
    data = {'Proportion Draw':np.ones(100)*DRAW_DIST[dist], 'Home':home, 'Away+Draw':rest, 'Profit':profit}
    temp_table = pd.DataFrame(data=data)
    profitTable = pd.concat([profitTable, temp_table], axis=0)
In [1371]:
profitTable
Out[1371]:
Proportion Draw Home Away+Draw Profit
0 0.0 0.000000 1.000000 0.015175
1 0.0 0.010101 0.989899 0.015077
2 0.0 0.020202 0.979798 0.014979
3 0.0 0.030303 0.969697 0.014881
4 0.0 0.040404 0.959596 0.014783
... ... ... ... ...
95 1.0 0.959596 0.040404 0.007659
96 1.0 0.969697 0.030303 0.007113
97 1.0 0.979798 0.020202 0.006566
98 1.0 0.989899 0.010101 0.006019
99 1.0 1.000000 0.000000 0.005472

1100 rows × 4 columns

In [1372]:
fig = px.line_3d(data_frame = profitTable, z='Profit', x='Home', y='Away+Draw', color='Proportion Draw')

fig.update_layout(title='Graph showing the effect of Bet Percentage on Expected Profit',
                    scene = dict(
                    xaxis_title='Home Bet Size %',
                    yaxis_title='Draw + Away Bet Size %',
                    zaxis_title='Expected Profit'),
                    width=1000,
                    height=700,
                    margin=dict(r=10, b=10, l=10, t=30))
fig.show(renderer=RENDER_MODE)

This line graph shows that betting behaviour, that is, the percentage of punters that bet on respective outcomes, changes the expected profit from this match.

The line represents expected profit as a function of Home Bet Size as % of total bets. Each line represents the expected profit curve when away and draw percentage are divided differently.

As seen from the graph above, given an arbitrary odds of Home=1.8, Draw=4, and Away=4.5, the maximum expected profit curve occurs when the pot less Home bets is placed on Draw (Navy Line).

Team Creator Application

In [1380]:
app = Dash(__name__)

# Initialize the app - incorporate a Dash Mantine theme
external_stylesheets = [dmc.theme.DEFAULT_COLORS]
app = Dash(__name__)

# App layout
app.layout = dmc.MantineProvider(
    theme={"colorScheme":"dark"},
    children=dmc.Container([
    dmc.Title('Match-up Creator', color="blue", size="h2", align='center'),
    dmc.Stack([
        dmc.Grid([
            dmc.Col([
                dmc.Select(
                label="Select Home Team",
                placeholder="Home Team",
                id="hometeam-select",
                searchable=True,
                clearable=True,
                data=ALL_TEAMS,
                style={"width": 'auto', "marginBottom": 10},
            ),
            ], span='auto'),
            dmc.Col([
                dmc.Select(
                label="Select Away Team",
                placeholder="Away Team",
                id="awayteam-select",
                searchable=True,
                clearable=True,
                data=ALL_TEAMS,
                style={"width": 'auto', "marginBottom": 10},
            ),
            ], span='auto'),
            ], align='center'),
        # dmc.Grid([
        #     dmc.Col([
        #         dmc.Select(
        #         label="Select Home Team",
        #         placeholder="Home Team",
        #         id="hometeam-select",
        #         searchable=True,
        #         clearable=True,
        #         data=ALL_TEAMS,
        #         style={"width": 'auto', "marginBottom": 10},
        #     ),
        #     ], span='auto'),
        #     dmc.Col([
        #         dmc.Select(
        #         label="Select Away Team",
        #         placeholder="Away Team",
        #         id="awayteam-select",
        #         searchable=True,
        #         clearable=True,
        #         data=ALL_TEAMS,
        #         style={"width": 'auto', "marginBottom": 10},
        #     ),
        #     ], span='auto'),
        #     ], align='center'),



        ]),

    ], fluid=True))

# Add controls to build the interaction
@callback(Output("hometeam-value", "children"), Input("hometeam-select", "value"))
def select_value(value):
    return value

@callback(Output("awayteam-value", "children"), Input("awayteam-select", "value"))
def select_value(value):
    return value

app.run(debug=True)